/*
 * Copyright (C) 2012-2022 NXP Semiconductors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "phUwbTypes.h"

#include "phOsalUwb.h"
#include "phUwb_BuildConfig.h"
#include "phNxpLogApis_TmlUwb.h"
#include "phUwbErrorCodes.h"
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <uwb_uwbs_tml_interface.h>

#define PAYLOAD_LEN_MSB 0x00
#define PAYLOAD_LEN_LSB 0x01
#define CRC_BYTES_LEN   0x02

#define UCI_RX_HDR_LEN             0x04
#define NORMAL_MODE_LEN_OFFSET     0x03
#define EXTND_LEN_INDICATOR_OFFSET 0x01
#define EXTENDED_LENGTH_OFFSET     0x02

#define UCI_HDR_LEN      0x04
#define HDLL_HEADER_SIZE 0x02

#define EXTND_LEN_INDICATOR_OFFSET_MASK 0x80
#define READ_IRQ_WAIT_TIME              (MAX_DELAY)
#define SR2XX_SET_FWD                   _IOW(0xEA, 0x04, long)

UWBStatus_t uwb_uwbs_tml_init(uwb_uwbs_tml_ctx_t *pCtx)
{
    UWBStatus_t status          = kUWBSTATUS_FAILED;
    uwb_bus_status_t bus_status = kUWB_bus_Status_FAILED;

    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        return kUWBSTATUS_INVALID_PARAMETER;
    }

    bus_status = uwb_bus_init(&pCtx->busCtx);
    if (bus_status != kUWB_bus_Status_OK) {
        status = kUWBSTATUS_CONNECTION_FAILED;
        LOG_E("Error: uwb_uwbs_tml_init(), uwb bus initialisation failed");
    }
    status = kUWBSTATUS_SUCCESS;
    return status;
}

UWBStatus_t uwb_uwbs_tml_deinit(uwb_uwbs_tml_ctx_t *pCtx)
{
    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        return kUWBSTATUS_INVALID_PARAMETER;
    }
    uwb_bus_deinit(&pCtx->busCtx);
    return UWBSTATUS_SUCCESS;
}

UWBStatus_t uwb_uwbs_tml_setmode(uwb_uwbs_tml_ctx_t *pCtx, uwb_uwbs_tml_mode_t mode)
{
    unsigned long protocol = 0;
    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        return kUWBSTATUS_INVALID_PARAMETER;
    }
    pCtx->mode = mode;
    if (mode == kUWB_UWBS_TML_MODE_HDLL) {
        protocol = 1;
    }

    if (ioctl(pCtx->busCtx.spi_fd, SR2XX_SET_FWD, protocol) != 0) {
        perror("query_apps ioctl set");
        return kUWBSTATUS_FAILED;
    }
    return kUWBSTATUS_SUCCESS;
}

UWBStatus_t uwb_uwbs_tml_data_tx(uwb_uwbs_tml_ctx_t *pCtx, uint8_t *pBuf, size_t bufLen)
{
    uwb_bus_status_t bus_status = kUWB_bus_Status_FAILED;
    UWBStatus_t status          = kUWBSTATUS_FAILED;
    pCtx->noOfBytesWritten      = -1;

    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        status = kUWBSTATUS_INVALID_PARAMETER;
        goto end;
    }

    if (pBuf == NULL || bufLen == 0) {
        LOG_E("write buffer is Null or bufLen is 0");
        status = kUWBSTATUS_INVALID_PARAMETER;
        goto end;
    }

    /*pre write delay to allow the device to recover from the last batch of data */
    phOsalUwb_Delay(2);
    bus_status = uwb_bus_data_tx(&pCtx->busCtx, pBuf, bufLen);
    if (bus_status == kUWB_bus_Status_FAILED) {
        LOG_E("uwb_bus_data_tx uwb_bus_data_tx failed");
        goto end;
    }

    pCtx->noOfBytesWritten = bufLen;
    status                 = kUWBSTATUS_SUCCESS;
end:
    return status;
}

UWBStatus_t uwb_uwbs_tml_data_rx(uwb_uwbs_tml_ctx_t *pCtx, uint8_t *pBuf, size_t *pBufLen)
{
    uwb_bus_status_t bus_status = kUWB_bus_Status_FAILED;
    UWBStatus_t status          = kUWBSTATUS_FAILED;
    uint16_t payloadLen;
    uint16_t totalBtyesToRead     = 0;
    uint32_t IsExtndLenIndication = 0;

    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        return kUWBSTATUS_INVALID_PARAMETER;
    }

    if (pBuf == NULL || pBufLen == NULL) {
        LOG_E("read buffer or bufLen is NULL");
        return kUWBSTATUS_INVALID_PARAMETER;
    }

    if (pCtx->mode == kUWB_UWBS_TML_MODE_HDLL) {
        if (*pBufLen == 0) {
            bus_status = uwb_bus_data_rx(&pCtx->busCtx, pBuf, HDLL_HEADER_SIZE);
            if (bus_status != kUWB_bus_Status_OK) {
                NXPLOG_UWB_TML_I("uwb_bus_data_rx failed 1");
                *pBufLen = 0;
                goto exit;
            }
            *pBufLen = pCtx->busCtx.bytesRead;
        }
        else {
            return kUWBSTATUS_INVALID_PARAMETER;
        }
        status = kUWBSTATUS_SUCCESS;
    }
    else if (pCtx->mode == kUWB_UWBS_TML_MODE_UCI) {
        *pBufLen = 0;
        // uwb_port_DelayinMicroSec(500);
        bus_status = uwb_bus_data_rx(&pCtx->busCtx, pBuf, UCI_RX_HDR_LEN);
        if (bus_status == kUWB_bus_Status_OK) {
            *pBufLen = pCtx->busCtx.bytesRead;
        }
        else {
            LOG_E("uwb_uwbs_tml_data_rx : reading from helios failed");
            *pBufLen = 0;
            goto exit;
        }
        status = kUWBSTATUS_SUCCESS;
    }
    else {
        LOG_E("uwb_uwbs_tml_data_tx : tml mode not supported");
    }
exit:
    uwb_port_DelayinMicroSec(50);

    return status;
}

UWBStatus_t uwb_uwbs_tml_data_trx(
    uwb_uwbs_tml_ctx_t *pCtx, uint8_t *pTxBuf, size_t txBufLen, uint8_t *pRxBuf, size_t *pRxBufLen)
{
    UWBStatus_t status = kUWBSTATUS_FAILED;

    if (pCtx->mode == kUWB_UWBS_TML_MODE_HDLL) {
        LOG_MAU8_D("HDLL Tx >", pTxBuf, txBufLen);
        *pRxBufLen = 0;
        status     = uwb_uwbs_tml_data_tx(pCtx, pTxBuf, txBufLen);
        if (status != kUWBSTATUS_SUCCESS) {
            LOG_E("uwb_uwbs_tml_hdll_data_trx write data failed");
            goto end;
        }
        status = uwb_uwbs_tml_data_rx(pCtx, pRxBuf, pRxBufLen);
        if (status != kUWBSTATUS_SUCCESS) {
            LOG_E("uwb_uwbs_tml_hdll_data_trx read data failed");
        }
        LOG_MAU8_D("HDLL Rx <", pRxBuf, *pRxBufLen);
    end:
        uwb_port_DelayinMicroSec(50);
    }
    else {
        /* not needed for UCI Mode */
    }
    return status;
}

UWBStatus_t uwb_uwbs_tml_data_trx_with_Len(
    uwb_uwbs_tml_ctx_t *pCtx, uint8_t *pTxBuf, size_t txBufLen, uint8_t *pRxBuf, size_t rxBufLen)
{
    UWBStatus_t status = kUWBSTATUS_FAILED;

    if (pRxBuf == NULL || rxBufLen == 0) {
        LOG_E("Response buffer is NULL or bytes to read is zero");
        return status;
    }

    if (pCtx->mode == kUWB_UWBS_TML_MODE_HDLL) {
        status = uwb_uwbs_tml_data_tx(pCtx, pTxBuf, txBufLen);
        if (status != kUWBSTATUS_SUCCESS) {
            LOG_E("uwb_uwbs_tml_HDLL_data_trx write data failed");
            goto end;
        }

        status = uwb_uwbs_tml_data_rx(pCtx, pRxBuf, &rxBufLen);
        if (status != kUWBSTATUS_SUCCESS) {
            LOG_E("uwb_uwbs_tml_HDLL_data_trx read data failed");
        }
        // LOG_MAU8_I("HDLL Rx >", pRxBuf , *pBufLen);

    end:
        uwb_port_DelayinMicroSec(50);
    }
    else {
        /* not needed for UCI Mode */
    }
    return status;
}

UWBStatus_t uwb_uwbs_tml_reset(uwb_uwbs_tml_ctx_t *pCtx)
{
    UWBStatus_t status = kUWBSTATUS_FAILED;
    uint8_t resp[20]   = {0};
    size_t resp_len    = 0;

    if (kUWB_bus_Status_OK != uwb_bus_reset(&pCtx->busCtx)) {
        return kUWBSTATUS_FAILED;
    }

    /* 1st Read HDLL Ready Notification */
    pCtx->mode = kUWB_UWBS_TML_MODE_HDLL;
    status     = uwb_uwbs_tml_data_rx(pCtx, resp, &resp_len);
    if (status != kUWBSTATUS_SUCCESS) {
        LOG_E("uwb_uwbs_tml_reset : uwb_uwbs_tml_data_rx failed");
        goto end;
    }

    // TODO: calculate crc and match with received one
    /* expected 0004810102007DC8 */
    if (resp_len == 0x08 && (*resp == 0x00) && (*(resp + 1) == 0x04) && (*(resp + 6) == 0x7D) &&
        (*(resp + 7) == 0xC8)) {
        /* Device is ready */
        status = kUWBSTATUS_SUCCESS;
    }
    else {
        status = kUWBSTATUS_FAILED;
    }
end:
    return status;
}

void uwb_uwbs_tml_flush_read_buffer(uwb_uwbs_tml_ctx_t *pCtx)
{
    /* Not needed here*/
    return;
}

UWBStatus_t uwb_uwbs_tml_helios_reset(uwb_uwbs_tml_ctx_t *pCtx, bool isFWDownloadDone)
{
    uwb_bus_reset(&pCtx->busCtx);
    return kUWBSTATUS_SUCCESS;
}

UWBStatus_t uwb_uwbs_tml_helios_get_hdll_edl_ntf(uwb_uwbs_tml_ctx_t *pCtx, uint8_t *pRxBuf, size_t *pRxBufLen)
{
    /* Dummy function. Not needed here. */
    return UWBSTATUS_SUCCESS;
}

UWBStatus_t uwb_uwbs_tml_helios_hardreset(uwb_uwbs_tml_ctx_t *pCtx)
{
    /* Dummy function. Not needed here. */
    return UWBSTATUS_SUCCESS;
}
